# Author: lbk
# Date: 2024-11-22
# Description: sftp文件上传
# Previous version: /

# 标准库
import os
import logging
import hashlib
import asyncio
import traceback
import configparser

# 第三方库
import asyncssh
from nptdms import TdmsWriter, ChannelObject

logging.basicConfig(format='%(asctime)s-%(filename)s-[line:%(lineno)d]-%(levelname)s: %(message)s', level=logging.DEBUG)

conf = configparser.ConfigParser()
conf.read('Config.ini', encoding='utf-8')
sftp_host = conf.get('TdmsFileConfig', 'host')
sftp_port = conf.getint('TdmsFileConfig', 'port')
sftp_user = conf.get('TdmsFileConfig', 'username')
sftp_pwd = conf.get('TdmsFileConfig', 'password')
sftp_rpath = conf.get('TdmsFileConfig', 'remote_path')
sftp_lpath = conf.get('TdmsFileConfig', 'local_path')
sftp_upload = conf.getint('TdmsFileConfig', 'upload_mark')



logging.debug((sftp_host, sftp_port, sftp_user, sftp_pwd, sftp_rpath, sftp_lpath, sftp_upload))

CHUNK_SIZE = 64 * 1024


async def makedirs(sftp, remote_path):
    """
    递归地创建多层目录
    :param sftp: SFTPClient 对象
    :param remote_path: 要创建的远程路径
    """
    try:
        # 尝试创建目录
        await sftp.mkdir(remote_path)
    except asyncssh.SFTPFailure as e:
        # 如果目录已经存在，忽略错误
        if e.reason != 'Failure':
            raise
    except asyncssh.SFTPNoSuchFile:
        # 如果父目录不存在，递归创建父目录
        parent_dir = os.path.dirname(remote_path)
        if parent_dir and parent_dir != '/':
            await makedirs(sftp, parent_dir)
            # 再次尝试创建当前目录
            await sftp.mkdir(remote_path)


async def compare_files(sftp, local_file_path, remote_file_path):
    # 读取本地文件的内容
    with open(local_file_path, 'rb') as local_file:
        local_content = local_file.read()

    # 读取远程文件的内容
    try:
        async with sftp.open(remote_file_path, 'rb') as remote_file:
            remote_content = await remote_file.read()
    except asyncssh.SFTPNoSuchFile:
        remote_content = b''

    # 计算本地文件和远程文件的哈希值
    local_hash = hashlib.sha256(local_content).hexdigest()
    remote_hash = hashlib.sha256(remote_content).hexdigest()
    # 如果哈希值不同，返回差异
    if local_hash != remote_hash:
        return local_content[len(remote_content):]
    else:
        return None


async def upload_file(hostname, port, username, password, local_file_path, remote_file_path, remote_path_dir):
    local_file_path = os.fsdecode(local_file_path)
    remote_file_path = os.fsdecode(remote_file_path)
    # 检查本地文件是否存在
    if not os.path.exists(local_file_path):
        raise FileNotFoundError("The local file does not exist: %s" % local_file_path)
    try:
        # 如果需要验证服务器的SSH密钥，请提供一个包含已知主机密钥的文件路径
        async with asyncssh.connect(hostname, port=port, username=username, password=password,
                                    known_hosts=None) as conn:
            # 打开SFTP会话
            async with conn.start_sftp_client() as sftp:
                await makedirs(sftp, remote_path_dir)  # 远程服务器创建文件夹
                # await sftp.put(local_file_path,remote_file_path)  #上传文件数据
                # 比较本地文件和远程文件
                diff_content = await compare_files(sftp, local_file_path, remote_file_path)
                # print(len(diff_content))
                if diff_content:
                    # 将差异部分追加到远程文件
                    async with sftp.open(remote_file_path, 'ab') as remote_file:
                        offset = 0
                        while offset < len(diff_content):
                            chunk = diff_content[offset:offset + CHUNK_SIZE]
                            await remote_file.write(chunk)
                            offset += CHUNK_SIZE

                    logging.info("Incremental update of '%s' to '%s' completed." % (local_file_path, remote_file_path))
                else:
                    logging.info("No changes detected in '%s'. No need to upload." % local_file_path)

                # print(f"File {local_file_path} uploaded to {remote_file_path} successfully.")
            # 关闭SFTP会话（通常会在连接关闭时自动关闭）
            # await sftp.close()
    except (OSError, asyncssh.Error) as exc:
        logging.debug(traceback.format_exc())
